import os
import csv
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
import windnd
from docx import Document
from openpyxl import load_workbook
from pptx import Presentation
import pythoncom
import win32com.client as win32
from threading import Thread

class FixedFileReplacerApp:
    def __init__(self, root):
        self.root = root
        self.root.title("文件内容批量替换工具 by: 风二西 2025.02.25")
        self.file_paths = []

        self.setup_ui()
        self.setup_dragdrop()

        # 支持的文件类型
        self.supported_ext = ['.txt', '.md', '.csv', '.docx', '.xlsx', '.pptx', '.doc', '.xls']

    def setup_ui(self):
        main_frame = ttk.Frame(self.root, padding=10)
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 控制面板
        control_frame = ttk.Frame(main_frame)
        control_frame.pack(fill=tk.X, pady=1)

        ttk.Label(control_frame, text="查找内容:").pack(side=tk.LEFT)
        self.old_text = tk.Entry(control_frame, width=20,bg="ivory")
        self.old_text.pack(side=tk.LEFT, padx=5,expand=True,fill=tk.BOTH)

        ttk.Label(control_frame, text="替换为:").pack(side=tk.LEFT)
        self.new_text = tk.Entry(control_frame, width=20,bg="ivory")
        self.new_text.pack(side=tk.LEFT, padx=5,expand=True,fill=tk.BOTH)

        # 修改按钮的command，使用thread123启动线程
        self.replace_btn = ttk.Button(control_frame, text="开始替换",
                                     command=lambda: self.thread123(self.start_replace))
        self.replace_btn.pack(side=tk.RIGHT, padx=5)

        # 日志区域
        self.log_area = scrolledtext.ScrolledText(main_frame, wrap=tk.WORD, state=tk.DISABLED,height=1,bg="Ivory")
        self.log_area.pack(fill=tk.BOTH, expand=True, pady=1,)
        self.log("支持的文件类型：txt, docx, xlsx, pptx, csv, md, doc, xls")

        # 底部工具栏
        bottom_frame = ttk.Frame(self.root)
        bottom_frame.pack(side=tk.BOTTOM, fill=tk.X, padx=10, pady=1)
        ttk.Button(bottom_frame, text="赞赏作者", command=self.donate_author).pack(side=tk.LEFT)
        ttk.Label(bottom_frame, text="更多工具、教程，欢迎关注bilibili风二西").pack(side=tk.RIGHT)

    def setup_dragdrop(self):
        """修复拖放功能"""

        def on_drop(files):
            try:
                paths = [f.decode('gb18030') for f in files]  # 更兼容的中文编码解码
                self.handle_dragged_files(paths)
            except Exception as e:
                self.log(f"拖放错误: {str(e)}")

        windnd.hook_dropfiles(self.root, func=on_drop)

    def donate_author(self):
        messagebox.showinfo("支持作者",
                            "感谢您的支持！\n支付宝/微信赞助码：123-456\n您的支持是我持续更新的动力！")

    def handle_dragged_files(self, paths):
        """修复文件添加逻辑"""
        new_files = []
        for path in paths:
            if os.path.isdir(path):
                for root_dir, _, filenames in os.walk(path):
                    for f in filenames:
                        full_path = os.path.join(root_dir, f)
                        if os.path.splitext(full_path)[1].lower() in self.supported_ext:
                            new_files.append(full_path)
            else:
                if os.path.splitext(path)[1].lower() in self.supported_ext:
                    new_files.append(path)

        # 去重处理
        for f in new_files:
            if f not in self.file_paths:
                self.file_paths.append(f)
                self.log(f"添加文件: {f}")
            else:
                self.log(f"文件已存在: {f}")

    def log(self, message):
        self.log_area.config(state=tk.NORMAL)
        self.log_area.insert(tk.END, message + "\n")
        self.log_area.see(tk.END)
        self.log_area.config(state=tk.DISABLED)

    def start_replace(self):
        """修复替换功能"""
        old_str = self.old_text.get().strip()
        new_str = self.new_text.get().strip()

        if not old_str:
            messagebox.showwarning("输入错误", "请输入要查找的内容")
            return

        total_replacements = 0
        for file_path in self.file_paths.copy():  # 使用副本遍历
            try:
                if not os.path.exists(file_path):
                    self.log(f"文件不存在: {file_path}")
                    self.file_paths.remove(file_path)
                    continue

                ext = os.path.splitext(file_path)[1].lower()
                replacements = 0

                self.log(f"\n开始处理: {file_path}")

                # 根据扩展名调用处理函数
                processor = {
                    '.txt': self.process_text_file,
                    '.md': self.process_text_file,
                    '.csv': self.process_csv,
                    '.docx': self.process_docx,
                    '.xlsx': self.process_xlsx,
                    '.pptx': self.process_pptx,
                    '.doc': self.process_old_office,
                    '.xls': self.process_old_office
                }.get(ext, lambda *_: 0)

                replacements = processor(file_path, old_str, new_str)
                total_replacements += replacements
                self.log(f"处理完成，替换 {replacements} 处")

            except Exception as e:
                self.log(f"处理失败: {str(e)}")
                self.file_paths.remove(file_path)  # 移除问题文件

        self.log(f"\n全部处理完成，总替换 {total_replacements} 处")

    def process_text_file(self, file_path, old_str, new_str):
        """修复文本文件处理"""
        replacements = 0
        try:
            # 自动检测编码
            encodings = ['utf-8', 'gb18030', 'gbk', 'utf-16']
            content = None
            for enc in encodings:
                try:
                    with open(file_path, 'r', encoding=enc) as f:
                        content = f.read()
                    break
                except UnicodeDecodeError:
                    continue

            if content is None:
                raise ValueError("无法识别文件编码")

            new_content = content.replace(old_str, new_str)
            replacements = content.count(old_str)

            # 使用原始编码保存
            with open(file_path, 'w', encoding=enc) as f:
                f.write(new_content)

            return replacements

        except Exception as e:
            self.log(f"文本处理错误: {str(e)}")
            return 0

    def process_csv(self, file_path, old_str, new_str):
        """修复CSV处理"""
        replacements = 0
        temp_file = file_path + ".tmp"

        try:
            # 检测文件编码
            encodings = ['utf-8', 'gb18030', 'gbk']
            for enc in encodings:
                try:
                    with open(file_path, 'r', newline='', encoding=enc) as f:
                        reader = csv.reader(f)
                        header = next(reader)
                    break
                except:
                    continue

            with open(file_path, 'r', newline='', encoding=enc) as infile, \
                    open(temp_file, 'w', newline='', encoding=enc) as outfile:

                reader = csv.reader(infile)
                writer = csv.writer(outfile)

                for row_idx, row in enumerate(reader):
                    new_row = []
                    for col_idx, cell in enumerate(row):
                        if old_str in cell:
                            count = cell.count(old_str)
                            new_cell = cell.replace(old_str, new_str)
                            replacements += count
                            new_row.append(new_cell)
                            self.log(f"CSV行{row_idx + 1}列{col_idx + 1}: 替换{count}处")
                        else:
                            new_row.append(cell)
                    writer.writerow(new_row)

            os.replace(temp_file, file_path)
            return replacements

        except Exception as e:
            if os.path.exists(temp_file):
                os.remove(temp_file)
            self.log(f"CSV处理错误: {str(e)}")
            return 0

    # 其他处理函数保持不变...

    def process_docx(self, file_path, old_str, new_str):
        """处理Word文档"""
        replacements = 0
        try:
            doc = Document(file_path)

            # 处理段落
            for para in doc.paragraphs:
                if old_str in para.text:
                    original = para.text
                    para.text = original.replace(old_str, new_str)
                    count = original.count(old_str)
                    replacements += count

            # 处理表格
            for table in doc.tables:
                for row in table.rows:
                    for cell in row.cells:
                        if old_str in cell.text:
                            original = cell.text
                            cell.text = original.replace(old_str, new_str)
                            count = original.count(old_str)
                            replacements += count

            doc.save(file_path)
            return replacements
        except Exception as e:
            self.log(f"DOCX处理错误: {str(e)}")
            return 0

    def process_xlsx(self, file_path, old_str, new_str):
        """处理Excel文档"""
        replacements = 0
        try:
            wb = load_workbook(file_path)
            for sheet in wb.worksheets:
                for row in sheet.iter_rows():
                    for cell in row:
                        if cell.value and (old_str in str(cell.value)):
                            original = str(cell.value)
                            new_value = original.replace(old_str, new_str)
                            count = original.count(old_str)
                            cell.value = new_value
                            replacements += count
            wb.save(file_path)
            return replacements
        except Exception as e:
            self.log(f"XLSX处理错误: {str(e)}")
            return 0

    def process_pptx(self, file_path, old_str, new_str):
        """处理PPT文档"""
        replacements = 0
        try:
            prs = Presentation(file_path)
            for slide in prs.slides:
                for shape in slide.shapes:
                    if hasattr(shape, "text") and shape.text:
                        if old_str in shape.text:
                            original = shape.text
                            shape.text = original.replace(old_str, new_str)
                            count = original.count(old_str)
                            replacements += count
            prs.save(file_path)
            return replacements
        except Exception as e:
            self.log(f"PPTX处理错误: {str(e)}")
            return 0

    def process_old_office(self, file_path, old_str, new_str):
        """处理旧版Office文档"""
        try:
            pythoncom.CoInitialize()
            if file_path.endswith('.doc'):
                return self.process_old_word(file_path, old_str, new_str)
            elif file_path.endswith('.xls'):
                return self.process_old_excel(file_path, old_str, new_str)
            return 0
        finally:
            pythoncom.CoUninitialize()

    def process_old_word(self, file_path, old_str, new_str):
        """处理旧版Word文档"""
        try:
            word = win32.gencache.EnsureDispatch('Word.Application')
            doc = word.Documents.Open(file_path)
            find = word.Selection.Find
            find.Text = old_str
            find.Replacement.Text = new_str
            find.Forward = True
            find.Wrap = 0  # wdFindContinue
            replace_count = find.Execute(Replace=2)  # wdReplaceAll
            doc.Save()
            doc.Close()
            word.Quit()
            return replace_count
        except Exception as e:
            self.log(f"旧版Word处理错误: {str(e)}")
            return 0

    def process_old_excel(self, file_path, old_str, new_str):
        """处理旧版Excel文档"""
        try:
            excel = win32.gencache.EnsureDispatch('Excel.Application')
            wb = excel.Workbooks.Open(file_path)
            replacements = 0
            for sheet in wb.Worksheets:
                used_range = sheet.UsedRange
                for cell in used_range:
                    if cell.Value and (old_str in str(cell.Value)):
                        original = str(cell.Value)
                        new_value = original.replace(old_str, new_str)
                        count = original.count(old_str)
                        cell.Value = new_value
                        replacements += count
            wb.Save()
            wb.Close()
            excel.Quit()
            return replacements
        except Exception as e:
            self.log(f"旧版Excel处理错误: {str(e)}")
            return 0

    @staticmethod
    def thread123(func, *args):
        '''将函数打包进线程'''
        # 创建
        t = Thread(target=func, args=args)
        # 守护线程，避免主线程退出后子线程继续运行
        t.daemon = True
        # 启动
        t.start()
if __name__ == "__main__":
    root = tk.Tk()
    app = FixedFileReplacerApp(root)
    root.geometry("500x200")
    root.mainloop()